package com.jlt.baidu.utils;

import java.util.ArrayList;
import java.util.List;

import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.core.MatOfPoint;
import org.opencv.core.MatOfPoint2f;
import org.opencv.core.Point;
import org.opencv.core.RotatedRect;
import org.opencv.core.Scalar;
import org.opencv.imgproc.Imgproc;

/**
 * 专门用于opencv图片旋转
 * 
 * 只是计算出旋转小于90度时的公式。当旋转大于90时，可以先把问题域换算到锐角的情况
 * 
 * 计算出某一直线的长度，然后与竖直线计算出夹角
 * 
 * @author Ives.Chen
 *
 */
public class OpencvRotateUtil {

    /**
     * 自动计算出图片的旋转角度，并旋转至正面位置
     * 
     * @param obj
     * @return
     */
    public static Mat handleMatRange(Mat obj) {
        Mat grayMat = new Mat();
        Mat lineMat = new Mat();
        // 存储检测出的直线坐标的矩阵，每个element有4个通道，第1、2个通道为直线的1个端点，第3、4个通道为直线的另1个端点
        Imgproc.cvtColor(obj, grayMat, Imgproc.COLOR_RGB2GRAY);
        // 灰度化
        Imgproc.Canny(grayMat, grayMat, 60, 200);
        // Canny边缘检测
        Imgproc.HoughLinesP(grayMat, lineMat, 5, 6, 9);
        // 获取最大矩形
        RotatedRect rect = findMaxRect(grayMat);
        // 旋转矩形 计算出来的角度固定+90
        double angle = Math.round(rect.angle);
        System.out.println("图片内容旋转角度：" + angle);
        return imageRotateLeftAny(obj, angle);
    }

    /**
     * 返回边缘检测之后的最大矩形,并返回
     * 
     * @param cannyMat Canny之后的mat矩阵
     * @return
     */
    public static RotatedRect findMaxRect(Mat cannyMat) {
        List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
        Mat hierarchy = new Mat();

        // 寻找轮廓
        Imgproc.findContours(cannyMat, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_NONE, new Point(0, 0));

        // 找出匹配到的最大轮廓
        double area = Imgproc.boundingRect(contours.get(0)).area();
        int index = 0;

        // 找出匹配到的最大轮廓
        for (int i = 0; i < contours.size(); i++) {
            double tempArea = Imgproc.boundingRect(contours.get(i)).area();
            if (tempArea > area) {
                area = tempArea;
                index = i;
            }
        }

        MatOfPoint2f matOfPoint2f = new MatOfPoint2f(contours.get(index).toArray());

        RotatedRect rect = Imgproc.minAreaRect(matOfPoint2f);

        return rect;
    }

    /**
     * 实现将图片内容旋转一定角度，不改变整体图片的宽，高。适合图片是正的，内容是倾斜的
     * 
     * @param cannyMat
     * @param rect
     * @return
     */
    public static Mat rotation(Mat cannyMat, RotatedRect rect) {
        // 获取矩形的四个顶点
        Point[] rectPoint = new Point[4];
        rect.points(rectPoint);

        double angle = Math.round(rect.angle);
        System.out.println("图片内容旋转角度：" + angle);

        Point center = rect.center;

        Mat CorrectImg = new Mat(cannyMat.size(), cannyMat.type());

        cannyMat.copyTo(CorrectImg);

        // 得到旋转矩阵算子
        Mat matrix = Imgproc.getRotationMatrix2D(center, angle, 1);

        Imgproc.warpAffine(cannyMat, CorrectImg, matrix, CorrectImg.size(), 1, 0, new Scalar(0, 0, 0));

        return CorrectImg;
    }

    /**
     * 实现图片整体向顺时针旋转任意角度，宽高变化，无损旋转。适合整体图片倾斜
     * 
     * @param splitImage
     * @param angle
     * @return
     */
    public static Mat imageRotateLeftAny(Mat splitImage, double angle) {
        double thera = angle * Math.PI / 180;
        double a = Math.sin(thera);
        double b = Math.cos(thera);

        int wsrc = splitImage.width();
        int hsrc = splitImage.height();

        int wdst = (int) (hsrc * Math.abs(a) + wsrc * Math.abs(b));
        int hdst = (int) (wsrc * Math.abs(a) + hsrc * Math.abs(b));
        Mat imgDst = new Mat(hdst, wdst, splitImage.type());

        Point pt = new Point(splitImage.cols() / 2, splitImage.rows() / 2);
        // 获取仿射变换矩阵
        Mat affineTrans = Imgproc.getRotationMatrix2D(pt, angle, 1.0);

        // 改变变换矩阵第三列的值
        affineTrans.put(0, 2, affineTrans.get(0, 2)[0] + (wdst - wsrc) / 2);
        affineTrans.put(1, 2, affineTrans.get(1, 2)[0] + (hdst - hsrc) / 2);

        Imgproc.warpAffine(splitImage, imgDst, affineTrans, imgDst.size(), Imgproc.INTER_CUBIC | Imgproc.WARP_FILL_OUTLIERS);
        return imgDst;
    }

    /**
     * 图片旋转，向右旋转90，图片宽高变化
     * 
     * @param obj
     * @return
     */
    public static Mat imageRotateRight90(Mat obj) {
        Mat tmp = new Mat();
        // 旋转函数
        Core.transpose(obj, tmp);
        Mat result = new Mat();
        // flipCode = 0 绕x轴旋转180， 也就是关于x轴对称
        // flipCode = 1 绕y轴旋转180， 也就是关于y轴对称
        // flipCode = -1 此函数关于原点对称
        Core.flip(tmp, result, 1);
        return result;
    }

    /**
     * 图片旋转，向左旋转90，图片宽高变化
     * 
     * @param obj
     * @return
     */
    public static Mat imageRotateLeft90(Mat obj) {
        Mat tmp = new Mat();
        // 旋转函数
        Core.transpose(obj, tmp);
        Mat result = new Mat();
        // flipCode = 0 绕x轴旋转180， 也就是关于x轴对称
        // flipCode = 1 绕y轴旋转180， 也就是关于y轴对称
        // flipCode = -1 此函数关于原点对称
        Core.flip(tmp, result, 0);
        return result;
    }

}
